home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pine / imap-3.0 / non-ANSI / c-client / tenex2.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-14  |  57.2 KB  |  2,003 lines

  1. /*
  2.  * Program:    Tenex mail routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    22 May 1990
  13.  * Last Edited:    15 July 1993
  14.  *
  15.  * Copyright 1993 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. #include <stdio.h>
  38. #include <ctype.h>
  39. #include <netdb.h>
  40. #include <errno.h>
  41. extern int errno;        /* just in case */
  42. #include "mail.h"
  43. #include "osdep.h"
  44. #include <pwd.h>
  45. #include <sys/file.h>
  46. #include <sys/stat.h>
  47. #include <sys/time.h>
  48. #include "tenex2.h"
  49. #include "rfc822.h"
  50. #include "misc.h"
  51.  
  52. /* Tenex mail routines */
  53.  
  54.  
  55. /* Driver dispatch used by MAIL */
  56.  
  57. DRIVER tenexdriver = {
  58.   "tenex",            /* driver name */
  59.   (DRIVER *) NIL,        /* next driver */
  60.   tenex_valid,            /* mailbox is valid for us */
  61.   tenex_parameters,        /* manipulate parameters */
  62.   tenex_find,            /* find mailboxes */
  63.   tenex_find_bboards,        /* find bboards */
  64.   tenex_find_all,        /* find all mailboxes */
  65.   tenex_find_all_bboards,    /* find all bboards */
  66.   tenex_subscribe,        /* subscribe to mailbox */
  67.   tenex_unsubscribe,        /* unsubscribe from mailbox */
  68.   tenex_subscribe_bboard,    /* subscribe to bboard */
  69.   tenex_unsubscribe_bboard,    /* unsubscribe from bboard */
  70.   tenex_create,            /* create mailbox */
  71.   tenex_delete,            /* delete mailbox */
  72.   tenex_rename,            /* rename mailbox */
  73.   tenex_open,            /* open mailbox */
  74.   tenex_close,            /* close mailbox */
  75.   tenex_fetchfast,        /* fetch message "fast" attributes */
  76.   tenex_fetchflags,        /* fetch message flags */
  77.   tenex_fetchstructure,        /* fetch message envelopes */
  78.   tenex_fetchheader,        /* fetch message header only */
  79.   tenex_fetchtext,        /* fetch message body only */
  80.   tenex_fetchbody,        /* fetch message body section */
  81.   tenex_setflag,        /* set message flag */
  82.   tenex_clearflag,        /* clear message flag */
  83.   tenex_search,            /* search for message based on criteria */
  84.   tenex_ping,            /* ping mailbox to see if still alive */
  85.   tenex_check,            /* check for new messages */
  86.   tenex_expunge,        /* expunge deleted messages */
  87.   tenex_copy,            /* copy messages to another mailbox */
  88.   tenex_move,            /* move messages to another mailbox */
  89.   tenex_append,            /* append string message to mailbox */
  90.   tenex_gc            /* garbage collect stream */
  91. };
  92.  
  93.                 /* prototype stream */
  94. MAILSTREAM tenexproto = {&tenexdriver};
  95.  
  96. /* Tenex mail validate mailbox
  97.  * Accepts: mailbox name
  98.  * Returns: our driver if name is valid, NIL otherwise
  99.  */
  100.  
  101. DRIVER *tenex_valid (name)
  102.     char *name;
  103. {
  104.   char tmp[MAILTMPLEN];
  105.   return tenex_isvalid (name,tmp) ? &tenexdriver : NIL;
  106. }
  107.  
  108.  
  109. /* Tenex mail test for valid mailbox
  110.  * Accepts: mailbox name
  111.  * Returns: T if valid, NIL otherwise
  112.  */
  113.  
  114. long tenex_isvalid (name,tmp)
  115.     char *name;
  116.     char *tmp;
  117. {
  118.   int i,fd;
  119.   char *s;
  120.   struct stat sbuf;
  121.   struct hostent *host_name;
  122.   if (!lhostn) {        /* have local host yet? */
  123.     gethostname(tmp,MAILTMPLEN);/* get local host name */
  124.     lhostn = cpystr ((host_name = gethostbyname (tmp)) ?
  125.              host_name->h_name : tmp);
  126.   }
  127.                 /* if file, get its status */
  128.   if ((*name != '{') && !((*name == '*') && (name[1] == '{')) &&
  129.       (stat (tenex_file (tmp,name),&sbuf) == 0)) {
  130.     if (sbuf.st_size != 0) {    /* if non-empty file */
  131.       if ((fd = open (tmp,O_RDONLY,NIL)) >= 0 && read (fd,tmp,64) >= 0) {
  132.     close (fd);        /* close the file */
  133.     if (s = strchr (tmp,'\n')) {
  134.       *s = '\0';        /* tie off header */
  135.                 /* must begin with dd-mmm-yy" */
  136.       if (((tmp[2] == '-' && tmp[6] == '-') ||
  137.            (tmp[1] == '-' && tmp[5] == '-')) &&
  138.           (s = strchr (tmp+20,',')) && strchr (s+2,';')) return LONGT;
  139.     }
  140.       }
  141.     }
  142.                 /* allow empty if a ".txt" file */
  143.     else if ((i = strlen (tmp)) > 4 && !strcmp (tmp + i - 4 ,".txt"))
  144.       return LONGT;
  145.   }
  146.   return NIL;            /* failed miserably */
  147. }
  148.  
  149.  
  150. /* Tenex manipulate driver parameters
  151.  * Accepts: function code
  152.  *        function-dependent value
  153.  * Returns: function-dependent return value
  154.  */
  155.  
  156. void *tenex_parameters (function,value)
  157.     long function;
  158.     void *value;
  159. {
  160.   fatal ("Invalid tenex_parameters function");
  161.   return NIL;
  162. }
  163.  
  164. /* Tenex mail find list of mailboxes
  165.  * Accepts: mail stream
  166.  *        pattern to search
  167.  */
  168.  
  169. void tenex_find (stream,pat)
  170.     MAILSTREAM *stream;
  171.     char *pat;
  172. {
  173.   void *s = NIL;
  174.   char *t,tmp[MAILTMPLEN];
  175.   while (t = sm_read (&s))    /* read subscription database */
  176.     if ((*t != '{') && (*t != '*') &&
  177.     strcmp (t,"INBOX") && pmatch (t,pat) &&    tenex_isvalid (t,tmp))
  178.       mm_mailbox (t);
  179. }
  180.  
  181.  
  182. /* Tenex mail find list of bboards
  183.  * Accepts: mail stream
  184.  *        pattern to search
  185.  */
  186.  
  187. void tenex_find_bboards (stream,pat)
  188.     MAILSTREAM *stream;
  189.     char *pat;
  190. {
  191.   void *s = NIL;
  192.   char *t,tmp[MAILTMPLEN];
  193.   while (t = sm_read (&s))    /* read subscription database */
  194.     if ((*t == '*') && (t[1] != '{') &&
  195.     pmatch (t+1,pat) && tenex_isvalid (t+1,tmp))
  196.       mm_bboard (t+1);
  197. }
  198.  
  199. /* Tenex mail find list of all mailboxes
  200.  * Accepts: mail stream
  201.  *        pattern to search
  202.  */
  203.  
  204. void tenex_find_all (stream,pat)
  205.     MAILSTREAM *stream;
  206.     char *pat;
  207. {
  208.   DIR *dirp;
  209.   struct direct *d;
  210.   char tmp[MAILTMPLEN],file[MAILTMPLEN];
  211.   int i = 0;
  212.   char *s,*t;
  213.   if (s = strrchr (pat,'/')) {    /* directory specified in pattern? */
  214.     strncpy (file,pat,i = (++s) - pat);
  215.     file[i] = '\0';        /* tie off prefix */
  216.     t = tenex_file (tmp,pat);    /* make fully-qualified file name */
  217.                 /* tie off directory name */
  218.     if (s = strrchr (t,'/')) *s = '\0';
  219.   }
  220.   else t = myhomedir ();    /* use home directory to search */
  221.   if (dirp = opendir (t)) {    /* now open that directory */
  222.     while (d = readdir (dirp)) {/* for each directory entry */
  223.       strcpy (file + i,d->d_name);
  224.       if (pmatch (file,pat) && (tenex_isvalid (file,tmp))) mm_mailbox (file);
  225.     }
  226.     closedir (dirp);        /* flush directory */
  227.   }
  228. }
  229.  
  230. /* Tenex mail find list of all bboards
  231.  * Accepts: mail stream
  232.  *        pattern to search
  233.  */
  234.  
  235. void tenex_find_all_bboards (stream,pat)
  236.     MAILSTREAM *stream;
  237.     char *pat;
  238. {
  239.   DIR *dirp;
  240.   struct direct *d;
  241.   struct passwd *pw;
  242.   char tmp[MAILTMPLEN],file[MAILTMPLEN];
  243.   int i = 1;
  244.   char *s;
  245.   if (!((pw = getpwnam ("ftp")) && pw->pw_dir)) return;
  246.   file[0] = '*';        /* bboard designator */
  247.                 /* directory specified in pattern? */
  248.   if (s = strrchr (pat,'/')) strncpy (file + 1,pat,i += (++s) - pat);
  249.   file[i] = '\0';        /* tie off prefix */
  250.   sprintf (tmp,"%s/%s",pw->pw_dir,(file[1] == '/') ? file + 2 : file + 1);
  251.   if (dirp = opendir (tmp)) {    /* now open that directory */
  252.     while (d = readdir (dirp)) {/* for each directory entry */
  253.       strcpy (file + i,d->d_name);
  254.       if (pmatch (file + 1,pat) && (tenex_isvalid (file,tmp)))
  255.     mm_bboard (file + 1);
  256.     }
  257.     closedir (dirp);        /* flush directory */
  258.   }
  259. }
  260.  
  261. /* Tenex mail subscribe to mailbox
  262.  * Accepts: mail stream
  263.  *        mailbox to add to subscription list
  264.  * Returns: T on success, NIL on failure
  265.  */
  266.  
  267. long tenex_subscribe (stream,mailbox)
  268.     MAILSTREAM *stream;
  269.     char *mailbox;
  270. {
  271.   char tmp[MAILTMPLEN];
  272.   return sm_subscribe (tenex_file (tmp,mailbox));
  273. }
  274.  
  275.  
  276. /* Tenex mail unsubscribe to mailbox
  277.  * Accepts: mail stream
  278.  *        mailbox to delete from subscription list
  279.  * Returns: T on success, NIL on failure
  280.  */
  281.  
  282. long tenex_unsubscribe (stream,mailbox)
  283.     MAILSTREAM *stream;
  284.     char *mailbox;
  285. {
  286.   char tmp[MAILTMPLEN];
  287.   return sm_unsubscribe (tenex_file (tmp,mailbox));
  288. }
  289.  
  290.  
  291. /* Tenex mail subscribe to bboard
  292.  * Accepts: mail stream
  293.  *        bboard to add to subscription list
  294.  * Returns: T on success, NIL on failure
  295.  */
  296.  
  297. long tenex_subscribe_bboard (stream,mailbox)
  298.     MAILSTREAM *stream;
  299.     char *mailbox;
  300. {
  301.   return NIL;            /* never valid for Tenex */
  302. }
  303.  
  304.  
  305. /* Tenex mail unsubscribe to bboard
  306.  * Accepts: mail stream
  307.  *        bboard to delete from subscription list
  308.  * Returns: T on success, NIL on failure
  309.  */
  310.  
  311. long tenex_unsubscribe_bboard (stream,mailbox)
  312.     MAILSTREAM *stream;
  313.     char *mailbox;
  314. {
  315.   return NIL;            /* never valid for Tenex */
  316. }
  317.  
  318. /* Tenex mail create mailbox
  319.  * Accepts: MAIL stream
  320.  *        mailbox name to create
  321.  * Returns: T on success, NIL on failure
  322.  */
  323.  
  324. long tenex_create (stream,mailbox)
  325.     MAILSTREAM *stream;
  326.     char *mailbox;
  327. {
  328.   char tmp[MAILTMPLEN];
  329.   int i,fd;
  330.                 /* must be a ".txt" file */
  331.   if ((i = strlen (mailbox)) < 4 || strcmp (mailbox + i - 4 ,".txt")) {
  332.     mm_log ("Can't create mailbox: name must end with .txt",ERROR);
  333.     return NIL;
  334.   }
  335.   if ((fd = open (tenex_file (tmp,mailbox),O_WRONLY|O_CREAT|O_EXCL,0600)) < 0){
  336.     sprintf (tmp,"Can't create mailbox %s: %s",mailbox,strerror (errno));
  337.     mm_log (tmp,ERROR);
  338.     return NIL;
  339.   }
  340.   close (fd);            /* close the file */
  341.   return LONGT;            /* return success */
  342. }
  343.  
  344.  
  345. /* Tenex mail delete mailbox
  346.  * Accepts: MAIL stream
  347.  *        mailbox name to delete
  348.  * Returns: T on success, NIL on failure
  349.  */
  350.  
  351. long tenex_delete (stream,mailbox)
  352.     MAILSTREAM *stream;
  353.     char *mailbox;
  354. {
  355.   return tenex_rename (stream,mailbox,NIL);
  356. }
  357.  
  358. /* Tenex mail rename mailbox
  359.  * Accepts: MAIL stream
  360.  *        old mailbox name
  361.  *        new mailbox name (or NIL for delete)
  362.  * Returns: T on success, NIL on failure
  363.  */
  364.  
  365. long tenex_rename (stream,old,new)
  366.     MAILSTREAM *stream;
  367.     char *old;
  368.     char *new;
  369. {
  370.   long ret = T;
  371.   char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
  372.   int ld;
  373.   int fd = open (tenex_file (file,old),O_RDONLY,NIL);
  374.                 /* lock out non c-client applications */
  375.   if (fd < 0) {            /* open mailbox */
  376.     sprintf (tmp,"Can't open mailbox %s: %s",old,strerror (errno));
  377.     mm_log (tmp,ERROR);
  378.     return NIL;
  379.   }
  380.                 /* get exclusive parse/append permission */
  381.   if ((ld = tenex_lock (fd,lock,LOCK_EX)) < 0) {
  382.     mm_log ("Unable to lock rename mailbox",ERROR);
  383.     return NIL;
  384.   }
  385.                 /* lock out other users */
  386.   if (flock (fd,LOCK_EX|LOCK_NB)) {
  387.     close (fd);            /* couldn't lock, give up on it then */
  388.     sprintf (tmp,"Mailbox %s is in use by another process",old);
  389.     mm_log (tmp,ERROR);
  390.     tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  391.     return NIL;
  392.   }
  393.                 /* do the rename or delete operation */
  394.   if (new ? rename (file,tenex_file (tmp,new)) : unlink (file)) {
  395.     sprintf (tmp,"Can't %s mailbox %s: %s",new ? "rename" : "delete",old,
  396.          strerror (errno));
  397.     mm_log (tmp,ERROR);
  398.     ret = NIL;            /* set failure */
  399.   }
  400.   flock (fd,LOCK_UN);        /* release lock on the file */
  401.   tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  402.   close (fd);            /* close the file */
  403.   return ret;            /* return success */
  404. }
  405.  
  406. /* Tenex mail open
  407.  * Accepts: stream to open
  408.  * Returns: stream on success, NIL on failure
  409.  */
  410.  
  411. MAILSTREAM *tenex_open (stream)
  412.     MAILSTREAM *stream;
  413. {
  414.   long i;
  415.   int fd,ld;
  416.   char *s,*t,*k;
  417.   char tmp[MAILTMPLEN];
  418.   struct stat sbuf;
  419.                 /* return prototype for OP_PROTOTYPE call */
  420.   if (!stream) return &tenexproto;
  421.   if (LOCAL) {            /* close old file if stream being recycled */
  422.     tenex_close (stream);    /* dump and save the changes */
  423.     stream->dtb = &tenexdriver;    /* reattach this driver */
  424.     mail_free_cache (stream);    /* clean up cache */
  425.   }
  426.   else {            /* flush old flagstring and flags if any */
  427.     if (stream->flagstring) fs_give ((void **) &stream->flagstring);
  428.     for (i = 0; i < NUSERFLAGS; ++i) stream->user_flags[i] = NIL;
  429.                 /* open .imapinit or .mminit file */
  430.     if ((fd = open (strcat (strcpy (tmp,myhomedir ()),"/.imapinit"),
  431.             O_RDONLY,NIL)) < 0)
  432.       fd = open (strcat (strcpy (tmp,myhomedir ()),"/.mminit"),O_RDONLY,NIL);
  433.     if (fd >= 0) {        /* got an init file? */
  434.       fstat (fd,&sbuf);        /* yes, get size */
  435.       read (fd,(s = stream->flagstring = (char *) fs_get (sbuf.st_size + 1)),
  436.         sbuf.st_size);    /* make readin area and read the file */
  437.       close (fd);        /* don't need the file any more */
  438.       s[sbuf.st_size] = '\0';    /* tie it off */
  439.                 /* parse init file */
  440.       while (*s && (t = strchr (s,'\n'))) {
  441.     *t = '\0';        /* tie off line, find second space */
  442.     if ((k = strchr (s,' ')) && (k = strchr (++k,' '))) {
  443.       *k = '\0';        /* tie off two words, is it "set keywords"? */
  444.       if (!strcmp (lcase (s),"set keywords")) {
  445.                 /* yes, get first keyword */
  446.         k = strtok (++k,", ");
  447.                 /* until parsed all keywords or empty */
  448.         for (i = 0; k && i < NUSERFLAGS; ++i) {
  449.                 /* set keyword, get next one */
  450.           stream->user_flags[i] = k;
  451.           k = strtok (NIL,", ");
  452.         }
  453.         break;        /* don't need to look at rest of file */
  454.       }
  455.     }
  456.     s = ++t;        /* try next line */
  457.       }
  458.     }
  459.   }
  460.  
  461.                 /* force readonly if bboard */
  462.   if (*stream->mailbox == '*') stream->readonly = T;
  463.   if (stream->readonly ||
  464.       (fd = open (tenex_file (tmp,stream->mailbox),O_RDWR,NIL)) < 0) {
  465.     if ((fd = open (tenex_file (tmp,stream->mailbox),O_RDONLY,NIL)) < 0) {
  466.       sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
  467.       mm_log (tmp,ERROR);
  468.       return NIL;
  469.     }
  470.     else if (!stream->readonly) {/* got it, but readonly */
  471.       mm_log ("Can't get write access to mailbox, access is readonly",WARN);
  472.       stream->readonly = T;
  473.     }
  474.   }
  475.   stream->local = fs_get (sizeof (TENEXLOCAL));
  476.                 /* note if an INBOX or not */
  477.   LOCAL->inbox = !strcmp (ucase (stream->mailbox),"INBOX");
  478.   if (*stream->mailbox != '*') {/* canonicalize the stream mailbox name */
  479.     fs_give ((void **) &stream->mailbox);
  480.     stream->mailbox = cpystr (tmp);
  481.   }
  482.                 /* get shared parse permission */
  483.   if ((ld = tenex_lock (fd,tmp,LOCK_SH)) < 0) {
  484.     mm_log ("Unable to lock open mailbox",ERROR);
  485.     return NIL;
  486.   }
  487.   flock(LOCAL->fd = fd,LOCK_SH);/* bind and lock the file */
  488.   tenex_unlock (ld,tmp);    /* release shared parse permission */
  489.   LOCAL->filesize = 0;        /* initialize parsed file size */
  490.   LOCAL->buf = (char *) fs_get (MAXMESSAGESIZE + 1);
  491.   LOCAL->buflen = MAXMESSAGESIZE;
  492.   stream->sequence++;        /* bump sequence number */
  493.                 /* parse mailbox */
  494.   stream->nmsgs = stream->recent = 0;
  495.   if (tenex_ping (stream) && !stream->nmsgs)
  496.     mm_log ("Mailbox is empty",(long) NIL);
  497.   return stream;        /* return stream to caller */
  498. }
  499.  
  500. /* Tenex mail close
  501.  * Accepts: MAIL stream
  502.  */
  503.  
  504. void tenex_close (stream)
  505.     MAILSTREAM *stream;
  506. {
  507.   if (stream && LOCAL) {    /* only if a file is open */
  508.     flock (LOCAL->fd,LOCK_UN);    /* unlock local file */
  509.     close (LOCAL->fd);        /* close the local file */
  510.                 /* free local text buffer */
  511.     if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
  512.                 /* nuke the local data */
  513.     fs_give ((void **) &stream->local);
  514.     stream->dtb = NIL;        /* log out the DTB */
  515.   }
  516. }
  517.  
  518.  
  519. /* Tenex mail fetch fast information
  520.  * Accepts: MAIL stream
  521.  *        sequence
  522.  */
  523.  
  524. void tenex_fetchfast (stream,sequence)
  525.     MAILSTREAM *stream;
  526.     char *sequence;
  527. {
  528.   return;            /* no-op for local mail */
  529. }
  530.  
  531.  
  532. /* Tenex mail fetch flags
  533.  * Accepts: MAIL stream
  534.  *        sequence
  535.  */
  536.  
  537. void tenex_fetchflags (stream,sequence)
  538.     MAILSTREAM *stream;
  539.     char *sequence;
  540. {
  541.   return;            /* no-op for local mail */
  542. }
  543.  
  544. /* Tenex mail fetch structure
  545.  * Accepts: MAIL stream
  546.  *        message # to fetch
  547.  *        pointer to return body
  548.  * Returns: envelope of this message, body returned in body value
  549.  *
  550.  * Fetches the "fast" information as well
  551.  */
  552.  
  553. ENVELOPE *tenex_fetchstructure (stream,msgno,body)
  554.     MAILSTREAM *stream;
  555.     long msgno;
  556.     BODY **body;
  557. {
  558.   LONGCACHE *lelt;
  559.   ENVELOPE **env;
  560.   BODY **b;
  561.   STRING bs;
  562.   char *hdr,*text;
  563.   unsigned long hdrsize;
  564.   unsigned long hdrpos = tenex_header (stream,msgno,&hdrsize);
  565.   unsigned long textsize = body ? tenex_size (stream,msgno) - hdrsize : 0;
  566.   unsigned long i = max (hdrsize,textsize);
  567.   if (stream->scache) {        /* short cache */
  568.     if (msgno != stream->msgno){/* flush old poop if a different message */
  569.       mail_free_envelope (&stream->env);
  570.       mail_free_body (&stream->body);
  571.     }
  572.     stream->msgno = msgno;
  573.     env = &stream->env;        /* get pointers to envelope and body */
  574.     b = &stream->body;
  575.   }
  576.   else {            /* long cache */
  577.     lelt = mail_lelt (stream,msgno);
  578.     env = &lelt->env;        /* get pointers to envelope and body */
  579.     b = &lelt->body;
  580.   }
  581.   if ((body && !*b) || !*env) {    /* have the poop we need? */
  582.     mail_free_envelope (env);    /* flush old envelope and body */
  583.     mail_free_body (b);
  584.     if (i > LOCAL->buflen) {    /* make sure enough buffer space */
  585.       fs_give ((void **) &LOCAL->buf);
  586.       LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
  587.     }
  588.                 /* get to header position */
  589.     lseek (LOCAL->fd,hdrpos,L_SET);
  590.                 /* read the header and text */
  591.     read (LOCAL->fd,hdr = (char *) fs_get (hdrsize + 1),hdrsize);
  592.     read (LOCAL->fd,text = (char *) fs_get (textsize + 1),textsize);
  593.     hdr[hdrsize] = '\0';    /* make sure tied off */
  594.     text[textsize] = '\0';    /* make sure tied off */
  595.     INIT (&bs,mail_string,(void *) text,textsize);
  596.                 /* parse envelope and body */
  597.     rfc822_parse_msg (env,body ? b : NIL,hdr,hdrsize,&bs,lhostn,LOCAL->buf);
  598.     fs_give ((void **) &text);
  599.     fs_give ((void **) &hdr);
  600.   }
  601.   if (body) *body = *b;        /* return the body */
  602.   return *env;            /* return the envelope */
  603. }
  604.  
  605. /* Tenex mail fetch message header
  606.  * Accepts: MAIL stream
  607.  *        message # to fetch
  608.  * Returns: message header in RFC822 format
  609.  */
  610.  
  611. char *tenex_fetchheader (stream,msgno)
  612.     MAILSTREAM *stream;
  613.     long msgno;
  614. {
  615.   unsigned long hdrsize;
  616.   unsigned long hdrpos = tenex_header (stream,msgno,&hdrsize);
  617.   char *s = (char *) fs_get (1 + hdrsize);
  618.   s[hdrsize] = '\0';        /* tie off string */
  619.                 /* get to header position */
  620.   lseek (LOCAL->fd,hdrpos,L_SET);
  621.   read (LOCAL->fd,s,hdrsize);    /* slurp the data */
  622.                 /* copy the string */
  623.   strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s,hdrsize);
  624.   fs_give ((void **) &s);    /* flush readin buffer */
  625.   return LOCAL->buf;
  626. }
  627.  
  628.  
  629. /* Tenex mail fetch message text (only)
  630.     body only;
  631.  * Accepts: MAIL stream
  632.  *        message # to fetch
  633.  * Returns: message text in RFC822 format
  634.  */
  635.  
  636. char *tenex_fetchtext (stream,msgno)
  637.     MAILSTREAM *stream;
  638.     long msgno;
  639. {
  640.   unsigned long hdrsize;
  641.   unsigned long hdrpos = tenex_header (stream,msgno,&hdrsize);
  642.   unsigned long textsize = tenex_size (stream,msgno) - hdrsize;
  643.   char *s = (char *) fs_get (1 + textsize);
  644.   s[textsize] = '\0';        /* tie off string */
  645.                 /* mark message as seen */
  646.   mail_elt (stream,msgno)->seen = T;
  647.                 /* recalculate status */
  648.   tenex_update_status (stream,msgno,T);
  649.                 /* get to text position */
  650.   lseek (LOCAL->fd,hdrpos + hdrsize,L_SET);
  651.   read (LOCAL->fd,s,textsize);    /* slurp the data */
  652.                 /* copy the string */
  653.   strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s,textsize);
  654.   fs_give ((void **) &s);    /* flush readin buffer */
  655.   return LOCAL->buf;
  656. }
  657.  
  658. /* Tenex fetch message body as a structure
  659.  * Accepts: Mail stream
  660.  *        message # to fetch
  661.  *        section specifier
  662.  *        pointer to length
  663.  * Returns: pointer to section of message body
  664.  */
  665.  
  666. char *tenex_fetchbody (stream,m,s,len)
  667.     MAILSTREAM *stream;
  668.     long m;
  669.     char *s;
  670.     unsigned long *len;
  671. {
  672.   BODY *b;
  673.   PART *pt;
  674.   char *t;
  675.   unsigned long i;
  676.   unsigned long base;
  677.   unsigned long offset = 0;
  678.   unsigned long hdrpos = tenex_header (stream,m,&base);
  679.   MESSAGECACHE *elt = mail_elt (stream,m);
  680.                 /* make sure have a body */
  681.   if (!(tenex_fetchstructure (stream,m,&b) && b && s && *s &&
  682.     ((i = strtol (s,&s,10)) > 0))) return NIL;
  683.   do {                /* until find desired body part */
  684.                 /* multipart content? */
  685.     if (b->type == TYPEMULTIPART) {
  686.       pt = b->contents.part;    /* yes, find desired part */
  687.       while (--i && (pt = pt->next));
  688.       if (!pt) return NIL;    /* bad specifier */
  689.                 /* note new body, check valid nesting */
  690.       if (((b = &pt->body)->type == TYPEMULTIPART) && !*s) return NIL;
  691.       offset = pt->offset;    /* get new offset */
  692.     }
  693.     else if (i != 1) return NIL;/* otherwise must be section 1 */
  694.                 /* need to go down further? */
  695.     if (i = *s) switch (b->type) {
  696.     case TYPEMESSAGE:        /* embedded message, calculate new base */
  697.       offset = b->contents.msg.offset;
  698.       b = b->contents.msg.body;    /* get its body, drop into multipart case */
  699.     case TYPEMULTIPART:        /* multipart, get next section */
  700.       if ((*s++ == '.') && (i = strtol (s,&s,10)) > 0) break;
  701.     default:            /* bogus subpart specification */
  702.       return NIL;
  703.     }
  704.   } while (i);
  705.                 /* lose if body bogus */
  706.   if ((!b) || b->type == TYPEMULTIPART) return NIL;
  707.   elt->seen = T;        /* mark message as seen */
  708.                 /* recalculate status */
  709.   tenex_update_status (stream,m,T);
  710.                 /* move to that place in the data */
  711.   lseek (LOCAL->fd,hdrpos + base + offset,L_SET);
  712.                 /* slurp the data */
  713.   t = (char *) fs_get (1 + b->size.ibytes);
  714.   read (LOCAL->fd,t,b->size.ibytes);
  715.   rfc822_contents(&LOCAL->buf,&LOCAL->buflen,len,t,b->size.ibytes,b->encoding);
  716.   fs_give ((void **) &t);    /* flush readin buffer */
  717.   return LOCAL->buf;
  718. }
  719.  
  720. /* Tenex locate header for a message
  721.  * Accepts: MAIL stream
  722.  *        message number
  723.  *        pointer to returned header size
  724.  * Returns: position of header in file
  725.  */
  726.  
  727. unsigned long tenex_header (stream,msgno,size)
  728.     MAILSTREAM *stream;
  729.     long msgno;
  730.     unsigned long *size;
  731. {
  732.   long siz;
  733.   long i = 0;
  734.   char c = '\0';
  735.   char *s = NIL;
  736.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  737.   long pos = elt->data1 + (elt->data2 >> 24);
  738.   long msiz = tenex_size (stream,msgno);
  739.                 /* is size known? */
  740.   if (!(*size = (elt->data2 & (unsigned long) 0xffffff))) {
  741.     lseek (LOCAL->fd,pos,L_SET);/* get to header position */
  742.                 /* search message for LF LF */
  743.     for (siz = 0; siz < msiz; siz++) {
  744.       if (--i <= 0)        /* read another buffer as necessary */
  745.     read (LOCAL->fd,s = LOCAL->buf,i = min (msiz-siz,(long) MAILTMPLEN));
  746.                 /* two newline sequence? */
  747.       if ((c == '\012') && (*s == '\012')) {
  748.                 /* yes, note for later */
  749.     elt->data2 |= (*size = siz + 1);
  750.     return pos;        /* return to caller */
  751.       }
  752.       else c = *s++;        /* next character */
  753.     }
  754.   }
  755.   return pos;            /* have position */
  756. }
  757.  
  758. /* Tenex mail set flag
  759.  * Accepts: MAIL stream
  760.  *        sequence
  761.  *        flag(s)
  762.  */
  763.  
  764. void tenex_setflag (stream,sequence,flag)
  765.     MAILSTREAM *stream;
  766.     char *sequence;
  767.     char *flag;
  768. {
  769.   MESSAGECACHE *elt;
  770.   long i;
  771.   long uf;
  772.   short f;
  773.                 /* no-op if no flags to modify */
  774.   if (!((f = tenex_getflags (stream,flag,&uf)) || uf)) return;
  775.                 /* get sequence and loop on it */
  776.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  777.     if ((elt = tenex_elt (stream,i))->sequence) {
  778.                 /* set all requested flags */
  779.       if (f&fSEEN) elt->seen = T;
  780.       if (f&fDELETED) elt->deleted = T;
  781.       if (f&fFLAGGED) elt->flagged = T;
  782.       if (f&fANSWERED) elt->answered = T;
  783.       elt->user_flags |= uf;
  784.                 /* recalculate status */
  785.       tenex_update_status (stream,i,NIL);
  786.     }
  787.                 /* make sure the update takes */
  788.   if (!stream->readonly) fsync (LOCAL->fd);
  789. }
  790.  
  791. /* Tenex mail clear flag
  792.  * Accepts: MAIL stream
  793.  *        sequence
  794.  *        flag(s)
  795.  */
  796.  
  797. void tenex_clearflag (stream,sequence,flag)
  798.     MAILSTREAM *stream;
  799.     char *sequence;
  800.     char *flag;
  801. {
  802.   MESSAGECACHE *elt;
  803.   long i;
  804.   long uf;
  805.   short f;
  806.                 /* no-op if no flags to modify */
  807.   if (!((f = tenex_getflags (stream,flag,&uf)) || uf)) return;
  808.                 /* get sequence and loop on it */
  809.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  810.     if ((elt = tenex_elt (stream,i))->sequence) {
  811.                 /* clear all requested flags */
  812.       if (f&fSEEN) elt->seen = NIL;
  813.       if (f&fDELETED) elt->deleted = NIL;
  814.       if (f&fFLAGGED) elt->flagged = NIL;
  815.       if (f&fANSWERED) elt->answered = NIL;
  816.       elt->user_flags &= ~uf;
  817.                 /* recalculate status */
  818.       tenex_update_status (stream,i,NIL);
  819.     }
  820.                 /* make sure the update takes */
  821.   if (!stream->readonly) fsync (LOCAL->fd);
  822. }
  823.  
  824. /* Tenex mail search for messages
  825.  * Accepts: MAIL stream
  826.  *        search criteria
  827.  */
  828.  
  829. void tenex_search (stream,criteria)
  830.     MAILSTREAM *stream;
  831.     char *criteria;
  832. {
  833.   long i,n;
  834.   char *d;
  835.   search_t f;
  836.                 /* initially all searched */
  837.   for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = T;
  838.                 /* get first criterion */
  839.   if (criteria && (criteria = strtok (criteria," "))) {
  840.                 /* for each criterion */
  841.     for (; criteria; (criteria = strtok (NIL," "))) {
  842.       f = NIL; d = NIL; n = 0;    /* init then scan the criterion */
  843.       switch (*ucase (criteria)) {
  844.       case 'A':            /* possible ALL, ANSWERED */
  845.     if (!strcmp (criteria+1,"LL")) f = tenex_search_all;
  846.     else if (!strcmp (criteria+1,"NSWERED")) f = tenex_search_answered;
  847.     break;
  848.       case 'B':            /* possible BCC, BEFORE, BODY */
  849.     if (!strcmp (criteria+1,"CC"))
  850.       f = tenex_search_string (tenex_search_bcc,&d,&n);
  851.     else if (!strcmp (criteria+1,"EFORE"))
  852.       f = tenex_search_date (tenex_search_before,&n);
  853.     else if (!strcmp (criteria+1,"ODY"))
  854.       f = tenex_search_string (tenex_search_body,&d,&n);
  855.     break;
  856.       case 'C':            /* possible CC */
  857.     if (!strcmp (criteria+1,"C"))
  858.       f = tenex_search_string (tenex_search_cc,&d,&n);
  859.     break;
  860.       case 'D':            /* possible DELETED */
  861.     if (!strcmp (criteria+1,"ELETED")) f = tenex_search_deleted;
  862.     break;
  863.       case 'F':            /* possible FLAGGED, FROM */
  864.     if (!strcmp (criteria+1,"LAGGED")) f = tenex_search_flagged;
  865.     else if (!strcmp (criteria+1,"ROM"))
  866.       f = tenex_search_string (tenex_search_from,&d,&n);
  867.     break;
  868.       case 'K':            /* possible KEYWORD */
  869.     if (!strcmp (criteria+1,"EYWORD"))
  870.       f = tenex_search_flag (tenex_search_keyword,&n,stream);
  871.     break;
  872.       case 'N':            /* possible NEW */
  873.     if (!strcmp (criteria+1,"EW")) f = tenex_search_new;
  874.     break;
  875.  
  876.       case 'O':            /* possible OLD, ON */
  877.     if (!strcmp (criteria+1,"LD")) f = tenex_search_old;
  878.     else if (!strcmp (criteria+1,"N"))
  879.       f = tenex_search_date (tenex_search_on,&n);
  880.     break;
  881.       case 'R':            /* possible RECENT */
  882.     if (!strcmp (criteria+1,"ECENT")) f = tenex_search_recent;
  883.     break;
  884.       case 'S':            /* possible SEEN, SINCE, SUBJECT */
  885.     if (!strcmp (criteria+1,"EEN")) f = tenex_search_seen;
  886.     else if (!strcmp (criteria+1,"INCE"))
  887.       f = tenex_search_date (tenex_search_since,&n);
  888.     else if (!strcmp (criteria+1,"UBJECT"))
  889.       f = tenex_search_string (tenex_search_subject,&d,&n);
  890.     break;
  891.       case 'T':            /* possible TEXT, TO */
  892.     if (!strcmp (criteria+1,"EXT"))
  893.       f = tenex_search_string (tenex_search_text,&d,&n);
  894.     else if (!strcmp (criteria+1,"O"))
  895.       f = tenex_search_string (tenex_search_to,&d,&n);
  896.     break;
  897.       case 'U':            /* possible UN* */
  898.     if (criteria[1] == 'N') {
  899.       if (!strcmp (criteria+2,"ANSWERED")) f = tenex_search_unanswered;
  900.       else if (!strcmp (criteria+2,"DELETED")) f = tenex_search_undeleted;
  901.       else if (!strcmp (criteria+2,"FLAGGED")) f = tenex_search_unflagged;
  902.       else if (!strcmp (criteria+2,"KEYWORD"))
  903.         f = tenex_search_flag (tenex_search_unkeyword,&n,stream);
  904.       else if (!strcmp (criteria+2,"SEEN")) f = tenex_search_unseen;
  905.     }
  906.     break;
  907.       default:            /* we will barf below */
  908.     break;
  909.       }
  910.       if (!f) {            /* if can't determine any criteria */
  911.     sprintf (LOCAL->buf,"Unknown search criterion: %.80s",criteria);
  912.     mm_log (LOCAL->buf,ERROR);
  913.     return;
  914.       }
  915.                 /* run the search criterion */
  916.       for (i = 1; i <= stream->nmsgs; ++i)
  917.     if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
  918.       mail_elt (stream,i)->searched = NIL;
  919.     }
  920.                 /* report search results to main program */
  921.     for (i = 1; i <= stream->nmsgs; ++i)
  922.       if (mail_elt (stream,i)->searched) mail_searched (stream,i);
  923.   }
  924. }
  925.  
  926. /* Tenex mail ping mailbox
  927.  * Accepts: MAIL stream
  928.  * Returns: T if stream still alive, NIL if not
  929.  */
  930.  
  931. long tenex_ping (stream)
  932.     MAILSTREAM *stream;
  933. {
  934.   long r = NIL;
  935.   int ld;
  936.   char lock[MAILTMPLEN];
  937.   if (stream && LOCAL) {    /* only if stream already open */
  938.                 /* get shared parse/append permission */
  939.     if ((ld = tenex_lock (LOCAL->fd,lock,LOCK_SH)) >= 0) {
  940.                 /* parse resulting mailbox */
  941.       r = (tenex_parse (stream)) ? T : NIL;
  942.       tenex_unlock (ld,lock);    /* release shared parse/append permission */
  943.     }
  944.                 /* snarf if this is a read-write inbox */
  945.     if (stream && LOCAL && LOCAL->inbox && !stream->readonly) {
  946.       tenex_snarf (stream);
  947.                 /* get shared parse/append permission */
  948.       if ((ld = tenex_lock (LOCAL->fd,lock,LOCK_SH)) >= 0) {
  949.                 /* parse resulting mailbox */
  950.     r = (tenex_parse (stream)) ? T : NIL;
  951.     tenex_unlock (ld,lock);    /* release shared parse/append permission */
  952.       }
  953.     }
  954.   }
  955.   return r;            /* return result of the parse */
  956. }
  957.  
  958.  
  959. /* Tenex mail check mailbox (too)
  960.     reparses status too;
  961.  * Accepts: MAIL stream
  962.  */
  963.  
  964. void tenex_check (stream)
  965.     MAILSTREAM *stream;
  966. {
  967.   long i = 1;
  968.   if (tenex_ping (stream)) {    /* ping mailbox */
  969.                 /* get new message status */
  970.     while (i <= stream->nmsgs) tenex_elt (stream,i++);
  971.     mm_log ("Check completed",(long) NIL);
  972.   }
  973. }
  974.  
  975. /* Tenex mail snarf messages from bezerk inbox
  976.  * Accepts: MAIL stream
  977.  */
  978.  
  979. void tenex_snarf (stream)
  980.     MAILSTREAM *stream;
  981. {
  982.   long i = 0;
  983.   long r,j;
  984.   struct stat sbuf;
  985.   struct iovec iov[2];
  986.   char lock[MAILTMPLEN];
  987.   MAILSTREAM *bezerk = NIL;
  988.   int ld = tenex_lock (LOCAL->fd,lock,LOCK_EX);
  989.   if (ld < 0) return;        /* give up if can't get exclusive permission */
  990.   mm_critical (stream);        /* go critical */
  991.                 /* calculate name of bezerk file */
  992.   sprintf (LOCAL->buf,MAILFILE,myusername ());
  993.   stat (LOCAL->buf,&sbuf);    /* see if anything there */
  994.   if (sbuf.st_size) {        /* non-empty? */
  995.     fstat (LOCAL->fd,&sbuf);    /* yes, get current file size */
  996.     if ((sbuf.st_size == LOCAL->filesize) &&
  997.     (bezerk = mail_open (bezerk,LOCAL->buf,OP_SILENT)) &&
  998.     (r = bezerk->nmsgs)) {    /* sizes match and can get bezerk mailbox? */
  999.                 /* yes, go to end of file in our mailbox */
  1000.       lseek (LOCAL->fd,sbuf.st_size,L_SET);
  1001.                 /* for each message in bezerk mailbox */
  1002.       while (r && (++i <= bezerk->nmsgs)) {
  1003.                 /* snarf message from Berkeley mailbox */
  1004.     iov[1].iov_base = bezerk_snarf (bezerk,i,&j);
  1005.                 /* calculate header line */
  1006.     mail_date ((iov[0].iov_base = LOCAL->buf),mail_elt (bezerk,i));
  1007.     sprintf (LOCAL->buf + strlen (LOCAL->buf),",%d;000000000000\n",
  1008.          iov[1].iov_len = j);
  1009.     iov[0].iov_len = strlen (iov[0].iov_base);
  1010.                 /* copy message to new mailbox */
  1011.     if (writev (LOCAL->fd,iov,2) < 0) {
  1012.       sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno));
  1013.       mm_log (LOCAL->buf,ERROR);
  1014.       ftruncate (LOCAL->fd,sbuf.st_size);
  1015.       r = 0;        /* flag that we have lost big */
  1016.     }
  1017.       }
  1018.       if (r) {            /* delete all the messages we copied */
  1019.     for (i = 1; i <= r; i++) mail_elt (bezerk,i)->deleted = T;
  1020.     mail_expunge (bezerk);/* now expunge all those messages */
  1021.       }
  1022.     }
  1023.     if (bezerk) mail_close (bezerk);
  1024.   }
  1025.   mm_nocritical (stream);    /* release critical */
  1026.   tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  1027. }
  1028.  
  1029. /* Tenex mail expunge mailbox
  1030.  * Accepts: MAIL stream
  1031.  */
  1032.  
  1033. void tenex_expunge (stream)
  1034.     MAILSTREAM *stream;
  1035. {
  1036.   int ld;
  1037.   unsigned long i = 1;
  1038.   unsigned long j,k,m,recent;
  1039.   unsigned long n = 0;
  1040.   unsigned long delta = 0;
  1041.   char lock[MAILTMPLEN];
  1042.   MESSAGECACHE *elt;
  1043.                 /* do nothing if stream dead */
  1044.   if (!tenex_ping (stream)) return;
  1045.   if (stream->readonly) {    /* won't do on readonly files! */
  1046.     mm_log ("Expunge ignored on readonly mailbox",WARN);
  1047.     return;
  1048.   }
  1049.   /* The cretins who designed flock() created a window of vulnerability in
  1050.    * upgrading locks from shared to exclusive or downgrading from exclusive
  1051.    * to shared.  Rather than maintain the lock at shared status at a minimum,
  1052.    * flock() actually *releases* the former lock.  Obviously they never talked
  1053.    * to any database guys.  Fortunately, we have the parse/append permission
  1054.    * lock.  If we require this lock before going exclusive on the mailbox,
  1055.    * another process can not sneak in and steal the exclusive mailbox lock on
  1056.    * us, because it will block on trying to get parse/append permission first.
  1057.    */
  1058.                 /* get exclusive parse/append permission */
  1059.   if ((ld = tenex_lock (LOCAL->fd,lock,LOCK_EX)) < 0) {
  1060.     mm_log ("Unable to lock expunge mailbox",ERROR);
  1061.     return;
  1062.   }
  1063.                 /* get exclusive access */
  1064.   if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
  1065.     flock (LOCAL->fd,LOCK_SH);    /* recover previous lock */
  1066.     mm_log("Can't expunge because mailbox is in use by another process",ERROR);
  1067.     tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  1068.     return;
  1069.   }
  1070.  
  1071.   mm_critical (stream);        /* go critical */
  1072.   recent = stream->recent;    /* get recent now that pinged and locked */
  1073.   while (i <= stream->nmsgs) {    /* for each message */
  1074.                 /* number of bytes to smash or preserve */
  1075.     k = ((elt = tenex_elt (stream,i))->data2 >> 24) + tenex_size (stream,i);
  1076.     if (elt->deleted) {        /* if deleted */
  1077.       if (elt->recent) --recent;/* if recent, note one less recent message */
  1078.       delta += k;        /* number of bytes to delete */
  1079.       mail_expunged (stream,i);    /* notify upper levels */
  1080.       n++;            /* count up one more expunged message */
  1081.     }
  1082.     else if (i++ && delta) {    /* preserved message */
  1083.       j = elt->data1;        /* first byte to preserve */
  1084.       do {            /* read from source position */
  1085.     m = min (k,LOCAL->buflen);
  1086.     lseek (LOCAL->fd,j,L_SET);
  1087.     read (LOCAL->fd,LOCAL->buf,(unsigned int) m);
  1088.                 /* write to destination position */
  1089.     lseek (LOCAL->fd,j - delta,L_SET);
  1090.     write (LOCAL->fd,LOCAL->buf,(unsigned int) m);
  1091.     j += m;            /* next chunk, perhaps */
  1092.       } while (k -= m);        /* until done */
  1093.       elt->data1 -= delta;    /* note the new address of this text */
  1094.     }
  1095.   }
  1096.   if (n) {            /* truncate file after last message */
  1097.     ftruncate (LOCAL->fd,LOCAL->filesize -= delta);
  1098.     sprintf (LOCAL->buf,"Expunged %ld messages",n);
  1099.                 /* output the news */
  1100.     mm_log (LOCAL->buf,(long) NIL);
  1101.   }
  1102.   else mm_log ("No messages deleted, so no update needed",(long) NIL);
  1103.   mm_nocritical (stream);    /* release critical */
  1104.                 /* notify upper level of new mailbox size */
  1105.   mail_exists (stream,stream->nmsgs);
  1106.   mail_recent (stream,recent);
  1107.   flock (LOCAL->fd,LOCK_SH);    /* allow sharers again */
  1108.   tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  1109. }
  1110.  
  1111. /* Tenex mail copy message(s)
  1112.     s;
  1113.  * Accepts: MAIL stream
  1114.  *        sequence
  1115.  *        destination mailbox
  1116.  * Returns: T if success, NIL if failed
  1117.  */
  1118.  
  1119. long tenex_copy (stream,sequence,mailbox)
  1120.     MAILSTREAM *stream;
  1121.     char *sequence;
  1122.     char *mailbox;
  1123. {
  1124.                 /* copy the messages */
  1125.   return (mail_sequence (stream,sequence)) ?
  1126.     tenex_copy_messages (stream,mailbox) : NIL;
  1127. }
  1128.  
  1129.  
  1130. /* Tenex mail move message(s)
  1131.     s;
  1132.  * Accepts: MAIL stream
  1133.  *        sequence
  1134.  *        destination mailbox
  1135.  * Returns: T if success, NIL if failed
  1136.  */
  1137.  
  1138. long tenex_move (stream,sequence,mailbox)
  1139.     MAILSTREAM *stream;
  1140.     char *sequence;
  1141.     char *mailbox;
  1142. {
  1143.   long i;
  1144.   MESSAGECACHE *elt;
  1145.   if (!(mail_sequence (stream,sequence) &&
  1146.     tenex_copy_messages (stream,mailbox))) return NIL;
  1147.                 /* delete all requested messages */
  1148.   for (i = 1; i <= stream->nmsgs; i++)
  1149.     if ((elt = tenex_elt (stream,i))->sequence) {
  1150.       elt->deleted = T;        /* mark message deleted */
  1151.                 /* recalculate status */
  1152.       tenex_update_status (stream,i,NIL);
  1153.     }
  1154.                 /* make sure the update takes */
  1155.   if (!stream->readonly) fsync (LOCAL->fd);
  1156.   return LONGT;
  1157. }
  1158.  
  1159. /* Tenex mail append message from stringstruct
  1160.  * Accepts: MAIL stream
  1161.  *        destination mailbox
  1162.  *        stringstruct of messages to append
  1163.  * Returns: T if append successful, else NIL
  1164.  */
  1165.  
  1166. long tenex_append (stream,mailbox,message)
  1167.     MAILSTREAM *stream;
  1168.     char *mailbox;
  1169.     STRING *message;
  1170. {
  1171.   struct stat sbuf;
  1172.   char c,*s,*t,tmp[MAILTMPLEN],lock[MAILTMPLEN];
  1173.   int fd,ld;
  1174.   long i = SIZE (message);
  1175.   long size = 0;
  1176.   long ret = LONGT;
  1177.                 /* N.B.: can't use LOCAL->buf for tmp */
  1178.                 /* make sure valid mailbox */
  1179.   if (!tenex_isvalid (mailbox,tmp)) {
  1180.     sprintf (tmp,"Not a Tenex-format mailbox: %s",mailbox);
  1181.     mm_log (tmp,ERROR);
  1182.     return NIL;
  1183.   }
  1184.   if ((fd = open (tenex_file (tmp,mailbox),O_RDWR|O_CREAT,
  1185.           S_IREAD|S_IWRITE)) < 0) {
  1186.     sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
  1187.     mm_log (tmp,ERROR);
  1188.     return NIL;
  1189.   }
  1190.                 /* get exclusive parse/append permission */
  1191.   if ((ld = tenex_lock (fd,lock,LOCK_EX)) < 0) {
  1192.     mm_log ("Unable to lock append mailbox",ERROR);
  1193.     return NIL;
  1194.   }
  1195.   s = fs_get (i + 1);        /* get space for the data */
  1196.                 /* copy the data w/o CR's */
  1197.   while (i--) if ((c = SNX (message)) != '\015') s[size++] = c;
  1198.  
  1199.   mm_critical (stream);        /* go critical */
  1200.   fstat (fd,&sbuf);        /* get current file size */
  1201.   lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
  1202.   rfc822_date (tmp);        /* get the date in RFC822 format */
  1203.   tmp[4] = '0';            /* in case 1-digit day */
  1204.   t = tmp[7] == ' ' ? tmp+5 : tmp+4;
  1205.   t[2] = t[6] = '-';        /* convert date string to Tenex format */
  1206.                 /* add remainder of header */
  1207.   sprintf (t+26,",%ld;000000000000\n",size);
  1208.                 /* write header */
  1209.   if ((write (fd,t,strlen (t)) < 0) || ((write (fd,s,size)) < 0)) {
  1210.     sprintf (tmp,"Message append failed: %s",strerror (errno));
  1211.     mm_log (tmp,ERROR);
  1212.     ftruncate (fd,sbuf.st_size);
  1213.     ret = NIL;
  1214.   }
  1215.   fsync (fd);            /* force out the update */
  1216.   tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  1217.   close (fd);            /* close the file */
  1218.   mm_nocritical (stream);    /* release critical */
  1219.   fs_give ((void **) &s);    /* flush the buffer */
  1220.   return ret;
  1221. }
  1222.  
  1223. /* Tenex garbage collect stream
  1224.  * Accepts: Mail stream
  1225.  *        garbage collection flags
  1226.  */
  1227.  
  1228. void tenex_gc (stream,gcflags)
  1229.     MAILSTREAM *stream;
  1230.     long gcflags;
  1231. {
  1232.   /* nothing here for now */
  1233. }
  1234.  
  1235. /* Internal routines */
  1236.  
  1237.  
  1238. /* Tenex mail lock file for parse/append permission
  1239.  * Accepts: file descriptor
  1240.  *        lock file name buffer
  1241.  *        type of locking operation (LOCK_SH or LOCK_EX)
  1242.  * Returns: file descriptor of lock or -1 if failure
  1243.  */
  1244.  
  1245. int tenex_lock (fd,lock,op)
  1246.     int fd;
  1247.     char *lock;
  1248.     int op;
  1249. {
  1250.   int ld;
  1251.   struct stat sbuf;
  1252.                 /* get data for this file */
  1253.   if (fstat (fd,&sbuf)) return -1;
  1254.                 /* make temporary file name */
  1255.   sprintf (lock,"/tmp/.%hx.%lx",sbuf.st_dev,sbuf.st_ino);
  1256.   if ((ld = open (lock,O_RDWR|O_CREAT,0666)) < 0) return NIL;
  1257.   flock (ld,op);        /* get this lock */
  1258.   return ld;            /* return locking file descriptor */
  1259. }
  1260.  
  1261.  
  1262. /* Tenex mail unlock file for parse/append permission
  1263.  * Accepts: file descriptor
  1264.  *        lock file name from tenex_lock()
  1265.  */
  1266.  
  1267. void tenex_unlock (fd,lock)
  1268.     int fd;
  1269.     char *lock;
  1270. {
  1271.   unlink (lock);        /* delete the file */
  1272.   flock (fd,LOCK_UN);        /* unlock it */
  1273.   close (fd);            /* close it */
  1274. }
  1275.  
  1276. /* Tenex mail return internal message size in bytes
  1277.  * Accepts: MAIL stream
  1278.  *        message #
  1279.  * Returns: internal size of message
  1280.  */
  1281.  
  1282. unsigned long tenex_size (stream,m)
  1283.     MAILSTREAM *stream;
  1284.     long m;
  1285. {
  1286.   MESSAGECACHE *elt = mail_elt (stream,m);
  1287.   return ((m < stream->nmsgs) ? mail_elt (stream,m+1)->data1 : LOCAL->filesize)
  1288.     - (elt->data1 + (elt->data2 >> 24));
  1289. }
  1290.  
  1291.  
  1292. /* Tenex mail generate file string
  1293.  * Accepts: temporary buffer to write into
  1294.  *        mailbox name string
  1295.  * Returns: local file string
  1296.  */
  1297.  
  1298. char *tenex_file (dst,name)
  1299.     char *dst;
  1300.     char *name;
  1301. {
  1302.   struct passwd *pw;
  1303.   char *s,*t,tmp[MAILTMPLEN];
  1304.   switch (*name) {
  1305.   case '*':            /* bboard? */
  1306.     sprintf (tmp,"~ftp/%s",(name[1] == '/') ? name+2 : name+1);
  1307.     dst = tenex_file (dst,tmp);/* recurse to get result */
  1308.     break;
  1309.   case '/':            /* absolute file path */
  1310.     strcpy (dst,name);        /* copy the mailbox name */
  1311.     break;
  1312.   case '~':            /* home directory */
  1313.     if (name[1] == '/') t = myhomedir ();
  1314.     else {
  1315.       strcpy (tmp,name + 1);    /* copy user name */
  1316.       if (s = strchr (tmp,'/')) *s = '\0';
  1317.       t = ((pw = getpwnam (tmp)) && pw->pw_dir) ? pw->pw_dir : "/NOSUCHUSER";
  1318.     }
  1319.     sprintf (dst,"%s%s",t,(s = strchr (name,'/')) ? s : "");
  1320.     break;
  1321.   default:            /* other name - INBOX becomes mail.txt */
  1322.     if (!strcmp (ucase (strcpy (dst,name)),"INBOX")) name = "mail.txt";
  1323.     sprintf (dst,"%s/%s",myhomedir (),name);
  1324.   }
  1325.   return dst;
  1326. }
  1327.  
  1328. /* Parse flag list
  1329.  * Accepts: MAIL stream
  1330.  *        flag list as a character string
  1331.  *        pointer to user flags to return
  1332.  * Returns: system flags
  1333.  */
  1334.  
  1335. long tenex_getflags (stream,flag,uf)
  1336.     MAILSTREAM *stream;
  1337.     char *flag;
  1338.     long *uf;
  1339. {
  1340.   char key[MAILTMPLEN];
  1341.   char *t,*s;
  1342.   short f = 0;
  1343.   long i;
  1344.   short j;
  1345.   *uf = 0;            /* initially no user flags */
  1346.   if (flag && *flag) {        /* no-op if no flag string */
  1347.                 /* check if a list and make sure valid */
  1348.     if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
  1349.       mm_log ("Bad flag list",ERROR);
  1350.       return NIL;
  1351.     }
  1352.                 /* copy the flag string w/o list construct */
  1353.     strncpy (LOCAL->buf,flag+i,(j = strlen (flag) - (2*i)));
  1354.     LOCAL->buf[j] = '\0';    /* tie off tail */
  1355.                 /* make uppercase, find first, parse */
  1356.     if (t = strtok (ucase (LOCAL->buf)," ")) do {
  1357.       i = 0;            /* no flag yet */
  1358.                 /* system flag, dispatch on first character */
  1359.       if (*t == '\\') switch (*++t) {
  1360.       case 'S':            /* possible \Seen flag */
  1361.     if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N' && t[4] == '\0')
  1362.       f |= i = fSEEN;
  1363.     break;
  1364.       case 'D':            /* possible \Deleted flag */
  1365.     if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
  1366.         t[5] == 'E' && t[6] == 'D' && t[7] == '\0') f |= i = fDELETED;
  1367.     break;
  1368.       case 'F':            /* possible \Flagged flag */
  1369.     if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
  1370.         t[5] == 'E' && t[6] == 'D' && t[7] == '\0') f |= i = fFLAGGED;
  1371.     break;
  1372.       case 'A':            /* possible \Answered flag */
  1373.     if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
  1374.         t[5] == 'R' && t[6] == 'E' && t[7] == 'D' && t[8] == '\0')
  1375.       f |= i = fANSWERED;
  1376.     break;
  1377.       default:            /* unknown */
  1378.     break;
  1379.       }
  1380.  
  1381.                 /* user flag, search through table */
  1382.       else for (j = 0; !i && j < NUSERFLAGS && (s =stream->user_flags[j]); ++j)
  1383.     if (!strcmp (t,ucase (strcpy (key,s)))) *uf |= i = 1 << j;
  1384.       if (!i) {            /* didn't find a matching flag? */
  1385.     sprintf (key,"Unknown flag: %.80s",t);
  1386.     mm_log (key,ERROR);
  1387.     *uf = NIL;        /* be sure no user flags returned */
  1388.     return NIL;        /* return no system flags */
  1389.       }
  1390.                 /* parse next flag */
  1391.     } while (t = strtok (NIL," "));
  1392.   }
  1393.   return f;
  1394. }
  1395.  
  1396. /* Tenex mail parse mailbox
  1397.  * Accepts: MAIL stream
  1398.  * Returns: T if parse OK
  1399.  *        NIL if failure, stream aborted
  1400.  */
  1401.  
  1402. long tenex_parse (stream)
  1403.     MAILSTREAM *stream;
  1404. {
  1405.   struct stat sbuf;
  1406.   MESSAGECACHE *elt = NIL;
  1407.   char *s,*t;
  1408.   char tmp[MAILTMPLEN];
  1409.   long i,j,msiz;
  1410.   long curpos = LOCAL->filesize;
  1411.   long nmsgs = stream->nmsgs;
  1412.   long recent = stream->recent;
  1413.   fstat (LOCAL->fd,&sbuf);    /* get status */
  1414.   if (sbuf.st_size < curpos) {    /* sanity check */
  1415.     mm_log ("Mailbox shrank!",ERROR);
  1416.     tenex_close (stream);
  1417.     return NIL;
  1418.   }
  1419.                 /* while there is stuff to parse */
  1420.   while (i = sbuf.st_size - curpos) {
  1421.                 /* get to that position in the file */
  1422.     lseek (LOCAL->fd,curpos,L_SET);
  1423.     if (!((read (LOCAL->fd,tmp,64) > 0) && (s = strchr (tmp,'\012')))) {
  1424.       sprintf (tmp,"Unable to read internal header line at %ld",curpos);
  1425.       mm_log (tmp,ERROR);
  1426.       tenex_close (stream);
  1427.       return NIL;
  1428.     }
  1429.     *s = '\0';            /* tie off header line */
  1430.     i = (s + 1) - tmp;        /* note start of text offset */
  1431.     if (!((s = strchr (tmp,',')) && (t = strchr (s+1,';')))) {
  1432.       sprintf (tmp,"Unable to parse internal header line at %ld",curpos);
  1433.       mm_log (tmp,ERROR);
  1434.       tenex_close (stream);
  1435.       return NIL;
  1436.     }
  1437.     *s++ = '\0'; *t++ = '\0';    /* tie off fields */
  1438.                 /* intantiate an elt for this message */
  1439.     elt = mail_elt (stream,++nmsgs);
  1440.     elt->data1 = curpos;    /* note file offset of header */
  1441.     elt->data2 = i << 24;    /* as well as offset from header of message */
  1442.                 /* parse the header components */
  1443.     if (!(mail_parse_date (elt,tmp) &&
  1444.       (elt->rfc822_size = msiz = strtol (s,&s,10)) && (!(s && *s)) &&
  1445.       isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
  1446.       isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
  1447.       isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
  1448.       isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])) {
  1449.       sprintf (tmp,"Unable to parse internal header line components at %ld",
  1450.            curpos);
  1451.       mm_log (tmp,ERROR);
  1452.       tenex_close (stream);
  1453.       return NIL;
  1454.     }
  1455.  
  1456.                 /* calculate system flags */
  1457.     if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
  1458.     if (j & fDELETED) elt->deleted = T;
  1459.     if (j & fFLAGGED) elt->flagged = T;
  1460.     if (j & fANSWERED) elt->answered = T;
  1461.     if (!(j & fOLD)) {        /* newly arrived message? */
  1462.       elt->recent = T;
  1463.       recent++;            /* count up a new recent message */
  1464.                 /* mark it as old */
  1465.       tenex_update_status (stream,nmsgs,NIL);
  1466.     }
  1467.                 /* start at first message byte */
  1468.     lseek (LOCAL->fd,curpos += i,L_SET);
  1469.     for (i = msiz; i;) {    /* for all bytes of the message */
  1470.                 /* read a buffer's worth */
  1471.       read (LOCAL->fd,tmp,j = min (i,(long) MAILTMPLEN));
  1472.       i -= j;            /* account for having read that much */
  1473.                 /* now count the CRLFs */
  1474.       while (j) if (tmp[--j] == '\n') elt->rfc822_size++;
  1475.     }
  1476.                 /* make sure didn't run off end of file */
  1477.     if ((curpos += msiz) > sbuf.st_size) {
  1478.       mm_log ("Last message runs past end of file",ERROR);
  1479.       tenex_close (stream);
  1480.       return NIL;
  1481.     }
  1482.   }
  1483.   fsync (LOCAL->fd);        /* make sure all the fOLD flags take */
  1484.                 /* update parsed file size */
  1485.   LOCAL->filesize = sbuf.st_size;
  1486.   mail_exists (stream,nmsgs);    /* notify upper level of new mailbox size */
  1487.   mail_recent (stream,recent);    /* and of change in recent messages */
  1488.   return LONGT;            /* return the winnage */
  1489. }
  1490.  
  1491. /* Tenex copy messages
  1492.  * Accepts: MAIL stream
  1493.  *        mailbox copy vector
  1494.  *        mailbox name
  1495.  * Returns: T if success, NIL if failed
  1496.  */
  1497.  
  1498. long tenex_copy_messages (stream,mailbox)
  1499.     MAILSTREAM *stream;
  1500.     char *mailbox;
  1501. {
  1502.   struct stat sbuf;
  1503.   MESSAGECACHE *elt;
  1504.   unsigned long i,j,k;
  1505.   int fd,ld;
  1506.   char lock[MAILTMPLEN];
  1507.                 /* make sure valid mailbox */
  1508.   if (!tenex_isvalid (mailbox,LOCAL->buf)) {
  1509.     sprintf (LOCAL->buf,"Not a Tenex-format mailbox: %s",mailbox);
  1510.     mm_log (LOCAL->buf,ERROR);
  1511.     return NIL;
  1512.   }
  1513.                 /* got file? */
  1514.   if ((fd = open (tenex_file (LOCAL->buf,mailbox),O_RDWR|O_CREAT,
  1515.           S_IREAD|S_IWRITE)) < 0) {
  1516.     sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno));
  1517.     mm_log (LOCAL->buf,ERROR);
  1518.     return NIL;
  1519.   }
  1520.   mm_critical (stream);        /* go critical */
  1521.                 /* get exclusive parse/append permission */
  1522.   if ((ld = tenex_lock (fd,lock,LOCK_EX)) < 0) {
  1523.     mm_log ("Unable to lock copy mailbox",ERROR);
  1524.     return NIL;
  1525.   }
  1526.   fstat (fd,&sbuf);        /* get current file size */
  1527.   lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
  1528.  
  1529.                 /* for each requested message */
  1530.   for (i = 1; i <= stream->nmsgs; i++)
  1531.     if ((elt = mail_elt (stream,i))->sequence) {
  1532.       lseek (LOCAL->fd,tenex_header (stream,i,&k),L_SET);
  1533.       j = 0;            /* mark first time through */
  1534.       do {            /* read from source position */
  1535.     if (j) {        /* get another message chunk */
  1536.       k = min (j,LOCAL->buflen);
  1537.       read (LOCAL->fd,LOCAL->buf,(unsigned int) k);
  1538.     }
  1539.     else {            /* first time through */
  1540.                 /* make a header */
  1541.       mail_date (LOCAL->buf,elt);
  1542.       sprintf (LOCAL->buf + strlen (LOCAL->buf),",%d;000000000000\012",
  1543.            j = tenex_size (stream,i));
  1544.                 /* add size of header string */
  1545.       j += (k = strlen (LOCAL->buf));
  1546.     }
  1547.     if (write (fd,LOCAL->buf,(unsigned int) k) < 0) {
  1548.       sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
  1549.       mm_log (LOCAL->buf,ERROR);
  1550.       ftruncate (fd,sbuf.st_size);
  1551.                 /* release exclusive parse/append permission */
  1552.       tenex_unlock (ld,lock);
  1553.       close (fd);        /* punt */
  1554.       mm_nocritical (stream);
  1555.       return NIL;
  1556.     }
  1557.       } while (j -= k);        /* until done */
  1558.     }
  1559.   fsync (fd);            /* force out the update */
  1560.   tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  1561.   close (fd);            /* close the file */
  1562.   mm_nocritical (stream);    /* release critical */
  1563.   return LONGT;
  1564. }
  1565.  
  1566. /* Tenex get cache element with status updating from file
  1567.  * Accepts: MAIL stream
  1568.  *        message number
  1569.  * Returns: cache element
  1570.  */
  1571.  
  1572. MESSAGECACHE *tenex_elt (stream,msgno)
  1573.     MAILSTREAM *stream;
  1574.     long msgno;
  1575. {
  1576.   unsigned long i,j;
  1577.   char c;
  1578.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1579.                 /* set the seek pointer */
  1580.   lseek (LOCAL->fd,(off_t) elt->data1 + (elt->data2 >> 24) - 13,L_SET);
  1581.                 /* read the new flags */
  1582.   if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
  1583.     sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
  1584.     fatal (LOCAL->buf);
  1585.   }
  1586.                 /* calculate system flags */
  1587.   elt->seen = (i = ((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0') & fSEEN ?
  1588.     T : NIL;
  1589.   elt->deleted = i & fDELETED ? T : NIL;
  1590.   elt->flagged = i & fFLAGGED ? T : NIL;
  1591.   elt->answered = i & fANSWERED ? T : NIL;
  1592.   c = LOCAL->buf[10];        /* remember first system flags byte */
  1593.   LOCAL->buf[10] = '\0';    /* tie off flags */
  1594.   j = strtol (LOCAL->buf,NIL,8);/* get user flags value */
  1595.   LOCAL->buf[10] = c;        /* restore first system flags byte */
  1596.                 /* set up all valid user flags (reversed!) */
  1597.   while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
  1598.         stream->user_flags[i]) elt->user_flags |= 1 << i;
  1599.   return elt;
  1600. }
  1601.  
  1602. /* Tenex update status string
  1603.  * Accepts: MAIL stream
  1604.  *        message number
  1605.  *        flag saying whether or not to sync
  1606.  */
  1607.  
  1608. void tenex_update_status (stream,msgno,syncflag)
  1609.     MAILSTREAM *stream;
  1610.     long msgno;
  1611.     long syncflag;
  1612. {
  1613.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1614.   unsigned long j,k = 0;
  1615.   if (!stream->readonly) {    /* not if readonly you don't */
  1616.     j = elt->user_flags;    /* get user flags */
  1617.                 /* reverse bits (dontcha wish we had CIRC?) */
  1618.     while (j) k |= 1 << (29 - find_rightmost_bit (&j));
  1619.                 /* print new flag string */
  1620.     sprintf (LOCAL->buf,"%010lo%02o",k,
  1621.          fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
  1622.          (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered));
  1623.                 /* get to that place in the file */
  1624.     lseek (LOCAL->fd,(off_t) elt->data1 + (elt->data2 >> 24) - 13,L_SET);
  1625.                 /* write new flags */
  1626.     write (LOCAL->fd,LOCAL->buf,12);
  1627.                 /* sync if requested */
  1628.     if (syncflag) fsync (LOCAL->fd);
  1629.   }
  1630. }
  1631.  
  1632. /* Search support routines
  1633.  * Accepts: MAIL stream
  1634.  *        message number
  1635.  *        pointer to additional data
  1636.  * Returns: T if search matches, else NIL
  1637.  */
  1638.  
  1639.  
  1640. char tenex_search_all (stream,msgno,d,n)
  1641.     MAILSTREAM *stream;
  1642.     long msgno;
  1643.     char *d;
  1644.     long n;
  1645. {
  1646.   return T;            /* ALL always succeeds */
  1647. }
  1648.  
  1649.  
  1650. char tenex_search_answered (stream,msgno,d,n)
  1651.     MAILSTREAM *stream;
  1652.     long msgno;
  1653.     char *d;
  1654.     long n;
  1655. {
  1656.   return mail_elt (stream,msgno)->answered ? T : NIL;
  1657. }
  1658.  
  1659.  
  1660. char tenex_search_deleted (stream,msgno,d,n)
  1661.     MAILSTREAM *stream;
  1662.     long msgno;
  1663.     char *d;
  1664.     long n;
  1665. {
  1666.   return mail_elt (stream,msgno)->deleted ? T : NIL;
  1667. }
  1668.  
  1669.  
  1670. char tenex_search_flagged (stream,msgno,d,n)
  1671.     MAILSTREAM *stream;
  1672.     long msgno;
  1673.     char *d;
  1674.     long n;
  1675. {
  1676.   return mail_elt (stream,msgno)->flagged ? T : NIL;
  1677. }
  1678.  
  1679.  
  1680. char tenex_search_keyword (stream,msgno,d,n)
  1681.     MAILSTREAM *stream;
  1682.     long msgno;
  1683.     char *d;
  1684.     long n;
  1685. {
  1686.   return mail_elt (stream,msgno)->user_flags & n ? T : NIL;
  1687. }
  1688.  
  1689.  
  1690. char tenex_search_new (stream,msgno,d,n)
  1691.     MAILSTREAM *stream;
  1692.     long msgno;
  1693.     char *d;
  1694.     long n;
  1695. {
  1696.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1697.   return (elt->recent && !elt->seen) ? T : NIL;
  1698. }
  1699.  
  1700. char tenex_search_old (stream,msgno,d,n)
  1701.     MAILSTREAM *stream;
  1702.     long msgno;
  1703.     char *d;
  1704.     long n;
  1705. {
  1706.   return mail_elt (stream,msgno)->recent ? NIL : T;
  1707. }
  1708.  
  1709.  
  1710. char tenex_search_recent (stream,msgno,d,n)
  1711.     MAILSTREAM *stream;
  1712.     long msgno;
  1713.     char *d;
  1714.     long n;
  1715. {
  1716.   return mail_elt (stream,msgno)->recent ? T : NIL;
  1717. }
  1718.  
  1719.  
  1720. char tenex_search_seen (stream,msgno,d,n)
  1721.     MAILSTREAM *stream;
  1722.     long msgno;
  1723.     char *d;
  1724.     long n;
  1725. {
  1726.   return mail_elt (stream,msgno)->seen ? T : NIL;
  1727. }
  1728.  
  1729.  
  1730. char tenex_search_unanswered (stream,msgno,d,n)
  1731.     MAILSTREAM *stream;
  1732.     long msgno;
  1733.     char *d;
  1734.     long n;
  1735. {
  1736.   return mail_elt (stream,msgno)->answered ? NIL : T;
  1737. }
  1738.  
  1739.  
  1740. char tenex_search_undeleted (stream,msgno,d,n)
  1741.     MAILSTREAM *stream;
  1742.     long msgno;
  1743.     char *d;
  1744.     long n;
  1745. {
  1746.   return mail_elt (stream,msgno)->deleted ? NIL : T;
  1747. }
  1748.  
  1749.  
  1750. char tenex_search_unflagged (stream,msgno,d,n)
  1751.     MAILSTREAM *stream;
  1752.     long msgno;
  1753.     char *d;
  1754.     long n;
  1755. {
  1756.   return mail_elt (stream,msgno)->flagged ? NIL : T;
  1757. }
  1758.  
  1759.  
  1760. char tenex_search_unkeyword (stream,msgno,d,n)
  1761.     MAILSTREAM *stream;
  1762.     long msgno;
  1763.     char *d;
  1764.     long n;
  1765. {
  1766.   return mail_elt (stream,msgno)->user_flags & n ? NIL : T;
  1767. }
  1768.  
  1769.  
  1770. char tenex_search_unseen (stream,msgno,d,n)
  1771.     MAILSTREAM *stream;
  1772.     long msgno;
  1773.     char *d;
  1774.     long n;
  1775. {
  1776.   return mail_elt (stream,msgno)->seen ? NIL : T;
  1777. }
  1778.  
  1779. char tenex_search_before (stream,msgno,d,n)
  1780.     MAILSTREAM *stream;
  1781.     long msgno;
  1782.     char *d;
  1783.     long n;
  1784. {
  1785.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1786.   return (char) ((long)((elt->year << 9) + (elt->month << 5) + elt->day) < n);
  1787. }
  1788.  
  1789.  
  1790. char tenex_search_on (stream,msgno,d,n)
  1791.     MAILSTREAM *stream;
  1792.     long msgno;
  1793.     char *d;
  1794.     long n;
  1795. {
  1796.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1797.   return (char) (((elt->year << 9) + (elt->month << 5) + elt->day) == n);
  1798. }
  1799.  
  1800.  
  1801. char tenex_search_since (stream,msgno,d,n)
  1802.     MAILSTREAM *stream;
  1803.     long msgno;
  1804.     char *d;
  1805.     long n;
  1806. {
  1807.                 /* everybody interprets "since" as .GE. */
  1808.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1809.   return (char) ((long)((elt->year << 9) + (elt->month << 5) + elt->day) >= n);
  1810. }
  1811.  
  1812.  
  1813. char tenex_search_body (stream,msgno,d,n)
  1814.     MAILSTREAM *stream;
  1815.     long msgno;
  1816.     char *d;
  1817.     long n;
  1818. {
  1819.   char ret;
  1820.   unsigned long hdrsize;
  1821.   unsigned long hdrpos = tenex_header (stream,msgno,&hdrsize);
  1822.   unsigned long textsize = tenex_size (stream,msgno) - hdrsize;
  1823.   char *s = (char *) fs_get (1 + textsize);
  1824.   s[textsize] = '\0';        /* tie off string */
  1825.                 /* recalculate status */
  1826.   tenex_update_status (stream,msgno,T);
  1827.                 /* get to text position */
  1828.   lseek (LOCAL->fd,hdrpos + hdrsize,L_SET);
  1829.   read (LOCAL->fd,s,textsize);    /* slurp the data */
  1830.                 /* copy the string */
  1831.   ret = search (s,textsize,d,n);/* do the search */
  1832.   fs_give ((void **) &s);    /* flush readin buffer */
  1833.   return ret;            /* return search value */
  1834. }
  1835.  
  1836.  
  1837. char tenex_search_subject (stream,msgno,d,n)
  1838.     MAILSTREAM *stream;
  1839.     long msgno;
  1840.     char *d;
  1841.     long n;
  1842. {
  1843.   char *s = tenex_fetchstructure (stream,msgno,NIL)->subject;
  1844.   return s ? search (s,(long) strlen (s),d,n) : NIL;
  1845. }
  1846.  
  1847.  
  1848. char tenex_search_text (stream,msgno,d,n)
  1849.     MAILSTREAM *stream;
  1850.     long msgno;
  1851.     char *d;
  1852.     long n;
  1853. {
  1854.   char ret;
  1855.   unsigned long hdrsize;
  1856.   unsigned long hdrpos = tenex_header (stream,msgno,&hdrsize);
  1857.   char *s = (char *) fs_get (1 + hdrsize);
  1858.   s[hdrsize] = '\0';        /* tie off string */
  1859.                 /* get to header position */
  1860.   lseek (LOCAL->fd,hdrpos,L_SET);
  1861.   read (LOCAL->fd,s,hdrsize);    /* slurp the data */
  1862.   ret = search (s,hdrsize,d,n) || tenex_search_body (stream,msgno,d,n);
  1863.   fs_give ((void **) &s);    /* flush readin buffer */
  1864.   return ret;            /* return search value */
  1865. }
  1866.  
  1867. char tenex_search_bcc (stream,msgno,d,n)
  1868.     MAILSTREAM *stream;
  1869.     long msgno;
  1870.     char *d;
  1871.     long n;
  1872. {
  1873.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1874.                 /* get text for address */
  1875.   rfc822_write_address (LOCAL->buf,
  1876.             tenex_fetchstructure (stream,msgno,NIL)->bcc);
  1877.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1878. }
  1879.  
  1880.  
  1881. char tenex_search_cc (stream,msgno,d,n)
  1882.     MAILSTREAM *stream;
  1883.     long msgno;
  1884.     char *d;
  1885.     long n;
  1886. {
  1887.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1888.                 /* get text for address */
  1889.   rfc822_write_address (LOCAL->buf,
  1890.             tenex_fetchstructure (stream,msgno,NIL)->cc);
  1891.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1892. }
  1893.  
  1894.  
  1895. char tenex_search_from (stream,msgno,d,n)
  1896.     MAILSTREAM *stream;
  1897.     long msgno;
  1898.     char *d;
  1899.     long n;
  1900. {
  1901.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1902.                 /* get text for address */
  1903.   rfc822_write_address (LOCAL->buf,
  1904.             tenex_fetchstructure (stream,msgno,NIL)->from);
  1905.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1906. }
  1907.  
  1908.  
  1909. char tenex_search_to (stream,msgno,d,n)
  1910.     MAILSTREAM *stream;
  1911.     long msgno;
  1912.     char *d;
  1913.     long n;
  1914. {
  1915.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1916.                 /* get text for address */
  1917.   rfc822_write_address (LOCAL->buf,
  1918.             tenex_fetchstructure (stream,msgno,NIL)->to);
  1919.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1920. }
  1921.  
  1922. /* Search parsers */
  1923.  
  1924.  
  1925. /* Parse a date
  1926.  * Accepts: function to return
  1927.  *        pointer to date integer to return
  1928.  * Returns: function to return
  1929.  */
  1930.  
  1931. search_t tenex_search_date (f,n)
  1932.     search_t f;
  1933.     long *n;
  1934. {
  1935.   long i;
  1936.   char *s;
  1937.   MESSAGECACHE elt;
  1938.                 /* parse the date and return fn if OK */
  1939.   return (tenex_search_string (f,&s,&i) && mail_parse_date (&elt,s) &&
  1940.       (*n = (elt.year << 9) + (elt.month << 5) + elt.day)) ? f : NIL;
  1941. }
  1942.  
  1943. /* Parse a flag
  1944.  * Accepts: function to return
  1945.  *        pointer to keyword integer to return
  1946.  *        MAIL stream
  1947.  * Returns: function to return
  1948.  */
  1949.  
  1950. search_t tenex_search_flag (f,n,stream)
  1951.     search_t f;
  1952.     long *n;
  1953.     MAILSTREAM *stream;
  1954. {
  1955.   short i;
  1956.   char *s,*t;
  1957.   if (t = strtok (NIL," ")) {    /* get a keyword */
  1958.     ucase (t);            /* get uppercase form of flag */
  1959.     for (i = 0; i < NUSERFLAGS && (s = stream->user_flags[i]); ++i)
  1960.       if (!strcmp (t,ucase (strcpy (LOCAL->buf,s))) && (*n = 1 << i)) return f;
  1961.   }
  1962.   return NIL;            /* couldn't find keyword */
  1963. }
  1964.  
  1965. /* Parse a string
  1966.  * Accepts: function to return
  1967.  *        pointer to string to return
  1968.  *        pointer to string length to return
  1969.  * Returns: function to return
  1970.  */
  1971.  
  1972.  
  1973. search_t tenex_search_string (f,d,n)
  1974.     search_t f;
  1975.     char **d;
  1976.     long *n;
  1977. {
  1978.   char *c = strtok (NIL,"");    /* remainder of criteria */
  1979.   if (c) {            /* better be an argument */
  1980.     switch (*c) {        /* see what the argument is */
  1981.     case '\0':            /* catch bogons */
  1982.     case ' ':
  1983.       return NIL;
  1984.     case '"':            /* quoted string */
  1985.       if (!(strchr (c+1,'"') && (*d = strtok (c,"\"")) && (*n = strlen (*d))))
  1986.     return NIL;
  1987.       break;
  1988.     case '{':            /* literal string */
  1989.       *n = strtol (c+1,&c,10);    /* get its length */
  1990.       if (*c++ != '}' || *c++ != '\015' || *c++ != '\012' ||
  1991.       *n > strlen (*d = c)) return NIL;
  1992.       c[*n] = '\255';        /* write new delimiter */
  1993.       strtok (c,"\255");    /* reset the strtok mechanism */
  1994.       break;
  1995.     default:            /* atomic string */
  1996.       *n = strlen (*d = strtok (c," "));
  1997.       break;
  1998.     }
  1999.     return f;
  2000.   }
  2001.   else return NIL;
  2002. }
  2003.